﻿using System;
using System.Collections;

using OpenTK;

using MosCraft.Utilities;
using MosCraft.Blocks;

namespace MosCraft
{
    public class Block
    {
        #region properties
        public static Block []blocksList;
        public static readonly bool []tickOnLoad = new bool[256];
        public static readonly bool []opaqueCubeLookup = new bool[256];
        public static readonly bool []isBlockContainer = new bool[256];
        public static readonly int[] lightOpacity = new int[256];
        public static readonly int[] lightValue = new int[256];
        public static readonly bool []requiresSelfNotify = new bool[256];
        public static readonly bool[] canBlockGrass;
        public static bool []useNeighborBrightness;

        public int textureUV;
        public readonly int blockID;
        internal float blockHardness;
        internal float blockResistance;

        internal Material blockMaterial;
        internal RenderMode renderMode;

        public Vector3d min;
        public Vector3d max;
        public float blockParticleGravity;
        public float slipperiness;
        private String blockName;
        #endregion

        #region Blocks
        public static readonly Block Stone;
        public static readonly Block Grass;
        public static readonly Block Dirt;
        public static readonly Block cobbleStone;
        public static readonly Block Bedrock;
        public static readonly Block Sand;
        public static readonly Block Gravel;

        public static readonly Block oreGold;
        public static readonly Block oreIron;
        public static readonly Block oreCoal;
        public static readonly Block oreLapis;
        public static readonly Block oreDiamond;

        public static readonly Block Planks;
        public static readonly Block Wood;
        public static readonly Block Leaves;
        public static readonly Block Reeds;
        public static readonly Block plantYellow;
        public static readonly Block plantRed;
        public static readonly BlockFlower mushroomBrown;
        public static readonly BlockFlower mushroomRed;

        public static readonly Block Water;
        #endregion

        static Block()
        {
            blocksList = new Block[256];
            canBlockGrass = new bool[256];
            useNeighborBrightness = new bool[256];

            Stone = (new Block(1, 1, Material.Rock)).setHardness(1.5F).setResistance(10F).setBlockName("stone");
            cobbleStone = (new Block(4, 16, Material.Rock)).setHardness(1.5F).setResistance(10F).setBlockName("cobblestone");
            Grass = (new BlockGrass(2)).setHardness(0.6F).setBlockName("grass");

            Dirt = (new Block(3, 2, Material.Ground)).setHardness(0.5F).setBlockName("dirt");
            Bedrock = (new Block(7, 17, Material.Rock)).setBlockUnbreakable().setResistance(6000000F).setBlockName("bedrock");
            Sand = (new Block(12, 18, Material.Sand)).setHardness(0.5F).setBlockName("sand");
            Gravel = (new Block(13, 19, Material.Sand)).setHardness(0.6F).setBlockName("gravel");

            oreGold = (new Block(14, 32, Material.Rock)).setHardness(3F).setResistance(5F).setBlockName("oreGold");
            oreIron = (new Block(15, 33, Material.Rock)).setHardness(3F).setResistance(5F).setBlockName("oreIron");
            oreCoal = (new Block(16, 34, Material.Rock)).setHardness(3F).setResistance(5F).setBlockName("oreCoal");
            oreLapis = (new Block(21, 160, Material.Rock)).setHardness(3F).setResistance(5F).setBlockName("oreLapis");
            oreDiamond = (new Block(56, 50, Material.Rock)).setHardness(3F).setResistance(5F).setBlockName("oreDiamond");

            Planks = (new Block(5, 4, Material.Wood)).setHardness(2.0F).setBlockName("planks");

            Wood = (new BlockLog(17)).setHardness(2.0F).setBlockName("log").setRequiresSelfNotify();
            Leaves = (new BlockLeaves(18)).setHardness(0.2F).setLightOpacity(1).setBlockName("leaves").setRequiresSelfNotify();

            Reeds = (new BlockReed(50)).setHardness(2.0F).setBlockName("reeds").setRequiresSelfNotify();
            plantYellow = (new BlockFlower(37, 13)).setHardness(0.0F).setBlockName("flower");
            plantRed = (new BlockFlower(38, 12)).setHardness(0.0F).setBlockName("rose");
            //mushroomBrown = (BlockFlower)(new BlockMushroom(39, 29)).setHardness(0.0F).setStepSound(soundGrassFootstep).setLightValue(0.125F).setBlockName("mushroom");
            //mushroomRed = (BlockFlower)(new BlockMushroom(40, 28)).setHardness(0.0F).setStepSound(soundGrassFootstep).setBlockName("mushroom");

            Water = (new BlockWater(40)).setBlockName("Water?");

            for(int i = 0; i < 256; i++)
            {
                if(blocksList[i] == null)
                {
                    continue;
                }
                bool flag = false;
                useNeighborBrightness[i] = flag;
            }

            canBlockGrass[0] = true;
        }

        /// <summary>
        /// Creates a new Block
        /// </summary>
        /// <param name="id">Unique identifier for the block</param>
        /// <param name="material">The Material group of the block</param>
        protected Block(int id, Material material)
        {
            blockMaterial = material;
            blocksList[id] = this;
            blockID = id;
            setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
            opaqueCubeLookup[id] = isOpaqueCube();
            lightOpacity[id] = isOpaqueCube() ? 255 : 0;
            //canBlockGrass[id] = !material.getCanBlockGrass();
            isBlockContainer[id] = false;
        }

        /// <summary>
        /// Creats a new block
        /// </summary>
        /// <param name="id">Unique identifier for the block</param>
        /// <param name="uv">Sets the blocks texture from the texture atlas 0-255</param>
        /// <param name="material">The Material group of the block</param>
        protected Block(int id, int uv, Material material) : this(id, material)
        {
            textureUV = uv;
        }

        internal Block setRequiresSelfNotify()
        {
            requiresSelfNotify[blockID] = true;
            return this;
        }

        internal Block setResistance(float f)
        {
            blockResistance = f * 3F;
            return this;
        }

        internal Block setHardness(float f)
        {
            blockHardness = f;
            if (blockResistance < f * 5F)
            {
                blockResistance = f * 5F;
            }
            return this;
        }

        /// <summary>
        /// Sets a block so it can'y be destoyed in survial
        /// </summary>
        /// <returns>Block class</returns>
        internal Block setBlockUnbreakable()
        {
            setHardness(-1F);
            return this;
        }

        /// <summary>
        /// Sets a block with a name
        /// </summary>
        /// <param name="blockname">String Block Name</param>
        /// <returns></returns>
        internal Block setBlockName(string blockname)
        {
            blockName = blockname;
            return this;
        }

        /// <summary>
        /// Gets the block name from the block class
        /// </summary>
        /// <returns>String block name</returns>
        internal String getBlockName()
        {
            return blockName;
        }

        internal Block setLightOpacity(int i)
        {
            lightOpacity[blockID] = i;
            return this;
        }

        internal Block setLightValue(float f)
        {
            lightValue[blockID] = (int)(15F * f);
            return this;
        }

        public virtual bool canPlaceBlockAt(World world, int x, int y, int z)
        {
            int blockId = world.getBlockID(x, y, z);
            return blockId == 0 || blocksList[blockId].blockMaterial.getIsGroundCover();
        }

        public virtual bool canBlockStay(World world, int x, int y, int z)
        {
            return true;
        }

        public bool canCollideCheck(int i, bool flag)
        {
            return isCollidable();
        }

        public bool isCollidable()
        {
            return true;
        }

        public virtual AxisAlignedBB getSelectedBoundingBoxFromPool(World world, int x, int y, int z)
        {
            return AxisAlignedBB.getBoundingBoxFromPool((double)x + min.X, (double)y + min.Y, (double)z + min.Z, (double)x + max.X, (double)y + max.Y, (double)z + max.Z);
        }

        public void getCollidingBoundingBoxes(World world, int x, int y, int z, AxisAlignedBB AxisAlignedBB, ref ArrayList ArrayList)
        {
            AxisAlignedBB axisalignedbb = getCollisionBoundingBoxFromPool(world, x, y, z);

            if (axisalignedbb != null && AxisAlignedBB.intersectsWith(axisalignedbb))
            {
                ArrayList.Add(axisalignedbb);
            }
        }

        public virtual AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z)
        {
            return AxisAlignedBB.getBoundingBoxFromPool((double)x + min.X, (double)y + min.Y, (double)z + min.Z, (double)x + max.X, (double)y + max.Y, (double)z + max.Z);
        }

        public void onNeighborBlockChange(World world, int x, int y, int z, int blockID)
        {
        }

        internal void initializeBlock()
        {
        }

        public float getHardness()
        {
            return blockHardness;
        }

        public void setBlockBounds(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
        {
            min.X = minX;
            min.Y = minY;
            min.Z = minZ;
            max.X = maxX;
            max.Y = maxY;
            max.Z = maxZ;
        }

        public int colorMultiplier(int x, int y, int z)
        {
            return 0xffffff;
        }

        public virtual bool isOpaqueCube()
        {
            return true;
        }

        public virtual int getBlockTexture(BlockFace face)
        {
            return textureUV;
        }

        public virtual bool RenderOpposites()
        {
            return false;
        }

        public virtual RenderMode getRenderType()
        {
            return RenderMode.STANDARD;
        }

        public virtual RenderPass getRenderBlockPass()
        {
            return RenderPass.Solids;
        }

        internal virtual bool renderAsNormalBlock()
        {
            return true;
        }
    }
}
